﻿#include "BaseModbus.h"
#include <QDebug>
#include <QMutexLocker>
#include <QElapsedTimer>
#include <QtConcurrent/QtConcurrent>

#include "iputils.h"

BaseModbus::BaseModbus(QObject *parent):BaseCommunication(parent)
{
    _isConnected=false;
}

BaseModbus::~BaseModbus()
{
    Dispose();
    modbus_close(m_ctx);
    modbus_free(m_ctx);
}

bool BaseModbus::ReadOutRegister(int addr, uint16_t &ret)
{
    QList<uint16_t> retList;
    bool ok=ReadOutRegisters(addr,1,retList);
    if(ok)
    {
        ret=retList.first();
        return true;
    }
    else
    {
        return false;
    }
}

bool BaseModbus::ReadInRegister(int addr, uint16_t &ret)
{
    QList<uint16_t> retList;
    bool ok=ReadInRegisters(addr,1,retList);
    if(ok)
    {
        ret=retList.first();
        return true;
    }
    else
    {
        return false;
    }
}

bool BaseModbus::DisConnect()
{
    if(m_ctx!=nullptr)
    {
        modbus_close(m_ctx);
        modbus_free(m_ctx);
        m_ctx=nullptr;
        _isConnected=false;
        return true;
    }
    return false;
}

bool BaseModbus::IsConnected()
{
    return _isConnected;
}

bool BaseModbus::Connect()
{
    int ret;
    CreateModbusInstance();
    int timeout=_other.contains("TimeoutMs")?_other["TimeoutMs"].toInt():3000;
    modbus_set_response_timeout(m_ctx,0,timeout*1000);
    //设置为1时断开重连有效，设置为0时程序会卡死
    //后来发现libmodbus版本问题，升级到新版本就行了
//    modbus_set_debug(m_ctx, 1);
    ret = modbus_set_error_recovery(m_ctx,static_cast<modbus_error_recovery_mode>(MODBUS_ERROR_RECOVERY_LINK|MODBUS_ERROR_RECOVERY_PROTOCOL));

    //设置slave地址
    modbus_set_slave(m_ctx, 1);
    ret = modbus_connect(m_ctx);
    _isConnected=(ret!=-1);
    if(!_isConnected&&_isFirstConnect)
    {
        _isFirstConnect=false;
        QtConcurrent::run([this]{
            AutoReconnect();
        });
    }
    return _isConnected;
}

void BaseModbus::Dispose()
{
    _isRunning=false;
//    wait();
}

void BaseModbus::AutoReconnect()
{
    //这样设计的目的是为了在程序结束时减少等待时间,减少检查_isRunning标志的时间
    QElapsedTimer ela;
    ela.start();
    while(_isRunning)
    {
        QThread::msleep(100);
        if(ela.elapsed()>10*1000)
        {
            bool connected=(modbus_connect(m_ctx) != -1);
            //如果连接状态发生变化，则记录日志
            if(_isConnected!=connected)
            {
                qInfo()<<_name<<GetCommunicationParam()<<"connecte:"<<connected;
            }

            _isConnected=connected;
            if(_isConnected)
            {
                return;
            }
            ela.restart();
        }
    }
}

bool BaseModbus::ReadOutRegisters(int addr, int count, QList<uint16_t> &retList)
{
    QMutexLocker locker(&_mutex);
    uint16_t *dest=new uint16_t[count];
    int retCount=modbus_read_registers(m_ctx,addr,count,dest);
    if(count==retCount)
    {
        SConvert(dest,retCount,retList);
    }
    locker.unlock();
    return count==retCount;
}

bool BaseModbus::ReadInRegisters(int addr, int count, QList<uint16_t> &retList)
{
    QMutexLocker locker(&_mutex);
    uint16_t *dest=new uint16_t[count];
    int retCount=modbus_read_input_registers(m_ctx,addr,count,dest);
    if(count==retCount)
    {
        SConvert(dest,retCount,retList);
    }
    locker.unlock();
    return count==retCount;
}

bool BaseModbus::WriteRegister(int addr, uint16_t input)
{
    QList<uint16_t> lst;
    lst<<input;
    return WriteRegisters(addr,1,lst);
}

bool BaseModbus::WriteRegisters(int addr, int count, QList<uint16_t> inputList)
{
    QMutexLocker locker(&_mutex);
    QList<uint16_t> list = inputList;
    uint16_t *data = new uint16_t[list.size()];
    for (int i = 0; i < list.size(); ++i)
    {
        data[i] = list.at(i);
    }
    int retCount=modbus_write_registers(m_ctx,addr,count,data);
    locker.unlock();
    return retCount=count;
}

bool BaseModbus::ReadOutBits(int addr, int count, QList<bool> &retList)
{
    QMutexLocker locker(&_mutex);
    uint8_t *dest=new uint8_t[count];
    int retCount=modbus_read_bits(m_ctx,addr,count,dest);
    if(retCount==count)
    {
        for(int i=0;i<count;i++)
        {
            retList.append(*dest);
            dest++;
        }
    }
    locker.unlock();
    return retCount==count;
}

bool BaseModbus::ReadInBits(int addr, int count, QList<bool> &retList)
{
    QMutexLocker locker(&_mutex);
    uint8_t *dest=new uint8_t[count];
    int retCount=modbus_read_input_bits(m_ctx,addr,count,dest);
    if(retCount==count)
    {
        for(int i=0;i<count;i++)
        {
            retList.append(*dest);
            dest++;
        }
    }
    locker.unlock();
    return retCount==count;
}

bool BaseModbus::WriteBits(int addr, int count, QList<bool> inputList)
{
    //功能暂时不明确->05功能码
    QMutexLocker locker(&_mutex);
    uint8_t *dest;

    for(int i=0;i<count;i++)
    {
        int status=inputList.at(i)?0xff:0x00;
        if(modbus_write_bit(m_ctx,addr+i,status)==-1)
        {
            return false;
        }
    }
    locker.unlock();
    return true;
}

